#!/usr/bin/python
# -*- coding: UTF-8 -*-

import json
import os
import glob
import platform
import subprocess
import shutil
import re
import sys
from string import Template
import mock_letter

def files(curr_dir='.', ext='*.exe'):
    for i in glob.glob(os.path.join(curr_dir, ext)):
        yield i


def remove_files(rootdir, ext, show=False):
    for i in files(rootdir, ext):
        os.remove(i)


def list_idl():
    idls = []
    for i in files("src/idl", "*.idl"):
        idls.append(os.path.basename(i))
    return idls

def add_pb_env():
    sysname = platform.system()

    if sysname == "Windows":
        myenv = os.environ
        bin_path = os.getcwd()
        bin_path = bin_path + "/thirdparty/bin;"
        myenv['path'] = bin_path + myenv['path']
    elif sysname == "Linux":
        envpath=os.getenv('PATH')
        envpath=os.getcwd()+'/thirdparty/bin:'+envpath
        os.environ['PATH']=envpath
    else:
        print("unsupported system!")
        return False
        
    return True


def check_pb_env():
    error = os.system("protoc --version")
    if error != 0:
        print("Protbuf not found")
        return False
    else:
        out_bytes = subprocess.check_output(['protoc', '--version'])
        out_text = out_bytes.decode('utf-8')
        strs = out_text.split(" ")
        if int(strs[1].split('.')[0]) < 3:
            print("Need Protobuf version >= 3.0")
            return False
    return True


def check_git_env():
    error = os.system("git --version")
    if error != 0:
        print("git not found")
        return False
    return True


def check_golang_env():
    error = os.system("go version")
    if error != 0:
        print("golang not install")
        return False
    else:
        out_bytes = subprocess.check_output(['go', 'version'])
        out_text = out_bytes.decode('utf-8')
        strs = out_text.split(" ")
        if int(strs[2].split('.')[1]) < 13:
            print("Need golang version 1.13+")
            return False
    return True


def check_compiler_env():
    error = os.system("cmake --version")
    if error != 0:
        print("cmake not found")
        return False
    else:
        out_bytes = subprocess.check_output(['cmake', '--version'])
        out_text = out_bytes.decode('utf-8')
        strs = out_text.split(" ")
        if int(strs[2].split(".")[0]) < 3:
            print("Need cmake version >= 3")
            return False
        else:
            if int(strs[2].split(".")[1]) < 5:
                print("Need cmake version >= 3.5")
                return False
    if platform.system() == "Windows":
        out_bytes = subprocess.check_output(['cmake', '-h'])
        out_text = out_bytes.decode('utf-8')
        if out_text.find("Visual Studio 16 2019") == -1:
            print("Visual Studio 2019 not found")
            return False
        else:
            print("Visual Studio 16 2019")
    else:
        error = os.system("gcc --version")
        if error != 0:
            error = os.system("clang --version")
            if error != 0:
                print("gcc not found")
                print("clang not found")
                return False
        else:
            out_bytes = subprocess.check_output(['gcc', '--version'])
            out_text = out_bytes.decode('utf-8')
            strs = out_text.split(" ")
            if int(strs[2].split(".")[0]) < 7:
                print("Need gcc version >= 7")
                return False
    return True


def check_frontend(initializer):
    if not os.path.exists("tmp"):
        os.mkdir("tmp")
    os.chdir("tmp")

    if os.path.exists("rpc-frontend"):
        os.chdir("rpc-frontend")
        if os.system("git pull") != 0:
            return False
        os.chdir("..")
    else:
        if os.system("git clone https://gitee.com/dennis-kk/rpc-frontend") != 0:
            print("git clone https://gitee.com/dennis-kk/rpc-frontend failed")
            return False

    initializer.build_frontend()
    os.chdir("..")

    return True


def json_has_attr(json_obj, attr_name):
    if json_obj is None:
        return False

    if attr_name is None:
        return False

    return (attr_name in json_obj) and (json_obj[attr_name] is not None)


def copy_dep_idl(basedir, idl_list, todir):
    if not basedir or basedir == "":
        basedir = "."

    if not todir or todir == "":
        todir = "."

    for idl in idl_list:
        dirifo = os.path.dirname(idl)
        if dirifo:
            pathcheck = todir
            for onedir in dirifo.split('/'):
                pathcheck += '/'+onedir
                if not os.path.exists(pathcheck):
                    os.mkdir(pathcheck)

        shutil.copyfile(basedir+'/'+idl, todir+'/'+idl)
    return True



# @title 递归处理idl的import问题
# @return 返回idl依赖列表 没有import关键字 则不处理
recom = re.compile(r'#import "([0-9a-zA-Z\_][0-9a-zA-Z\_\.\\/]*)"')  # 正则对应处理对应import

def open_file(path):
    if sys.version.startswith('2'):
        return open(path)
    else:
        return open(path, "r", encoding="utf-8")

# @title 处理idl依赖关系
# @detail 这里约定最外层的文件夹不进行拷贝，但是idl子文件夹的引用关系要保持，因为import实现语义的问题
# 其实这里有个思考我是否真的需要repo具备add这个语义？
def deal_idl_import(filename):
    oldpath = os.getcwd()
    basedir = os.path.dirname(filename)  # 基础路径不纳入拷贝
    basename = os.path.basename(filename)  # 基础路径的子路径纳入拷贝

    if basedir != "" and basedir != ".":
        os.chdir(basedir)

    idl_list = anlays_import("", basename)
    os.chdir(oldpath)
    return idl_list

# @title 递归函数，返回设置了调用关系的子路径
def anlays_import(dir, basename):
    if dir and dir != "" and dir != ".":
        basename = dir + '/' + basename

    fhandle = open_file(basename)
    if not fhandle:
        print("can't not open idl file: " + basename)
        return

    idl_list = []
    for line in fhandle:
        line = line.lstrip()
        # search for starting with #import
        if not line or line == "":
            continue

        if line[0] == "#":
            matchres = recom.match(line)
            if not matchres:
                continue
            cdir = os.path.dirname(matchres.groups()[0])
            cname = os.path.basename(matchres.groups()[0])

            if dir != "" and dir != ".":
                idl_list.append(dir + "/" + matchres.groups()[0])
            else:
                idl_list.append(matchres.groups()[0])

            searchdir = ""
            if cdir != "" and cdir != ".":
                searchdir = cdir
            if dir != "" and dir != ".":
                searchdir = dir + '/' + searchdir

            for one in anlays_import(searchdir, cname):
                if dir != "" and dir != ".":
                    idl_list.append(dir + "/" + one)
                else:
                    idl_list.append(one)

    fhandle.close()
    return idl_list

# @detail 检查service 是否生成
def check_service_build(idlname, sname, is_dynamic):
    lib_path = './lib/proxy/{idlname}/{sname}/Debug/lib{sname}_proxy.a'.format(idlname=idlname, sname=sname)
    if not os.path.exists(lib_path):
        return False
    
    lib_path = './lib/stub/{idlname}/{sname}/Debug/lib{sname}_stub.a'.format(idlname=idlname, sname=sname)
    if not os.path.exists(lib_path):
        return False

    lib_path = './lib/stub/{idlname}/{sname}/Debug/lib{sname}'.format(idlname=idlname, sname=sname)
    if is_dynamic:
        lib_path +='.so'
    else:
        lib_path +='.a'

    if not os.path.exists(lib_path):
        return False
    return True

# @detail 生成proxy 测试文件
def gen_test_proxy(base_path, base_name, service_name, methods, depst, depen):

    # 拷贝mock trans port
    shutil.copyfile('./mock/cmock/src/mock_transport.h', base_path + '/mock_transport.h')
    shutil.copyfile('./mock/cmock/src/mock_transport.cpp', base_path + '/mock_transport.cpp')
    shutil.copyfile('./mock/cmock/src/mock_logger.h', base_path + '/mock_logger.h')

    # 生成cmake
    proxy_temp = Template(open('./mock/template/proxy_cmake.template', 'r').read())
    proxy_file = open(base_path + '/CMakeLists.txt', 'w')
    proxy_file.write(proxy_temp.substitute(base_name=base_name, service_name=service_name, execname=service_name.lower()))
    proxy_file.close()

    # 生成proxy.cpp
    proxy_temp = Template(open('./mock/template/proxy_main.template','r').read())
    proxy_file = open(base_path+'/proxy.cpp', 'w')
    proxy_file.write(proxy_temp.substitute(service_name=service_name,execname=service_name.lower()))
    proxy_file.close()

    # 生成mock_caller.h
    proxy_temp = Template(open('./mock/template/proxy_caller_header.template','r').read())
    proxy_file = open(base_path+'/{service_name}_caller.h'.format(service_name=service_name.lower()), 'w')
    proxy_file.write(proxy_temp.substitute(service_name=service_name,base_name=base_name))
    proxy_file.close()

    # 生成call.cpp
    # 先生成 header
    proxy_file = open(base_path + '/{service_name}_caller.cpp'.format(service_name=service_name.lower()), 'w')
    proxy_temp = Template(mock_letter.caller_include_letter)
    proxy_file.write(proxy_temp.substitute(execname=service_name.lower()))

    # 根据结构体生成结构体的依赖
    for st in depst:
        proxy_file.write('#include "' + st + '_struct.h"\n')

    if depen is not None:
        proxy_file.write('#include "' + base_name + '.enum.h"\n')

    # 再生成基础的body
    proxy_temp = Template(mock_letter.caller_body_letter)
    proxy_file.write(proxy_temp.substitute(service_name=service_name))

    # 最后针对每个方法生成协程调用
    proxy_file.write('\tco(\n')
    for method in methods:
        proxy_file.write('\t\tprx_->{mname}('.format(mname=method['name']))
        n=1
        for arg in method['arguments']:
            if n != 1:
                proxy_file.write(',')

            if arg['IdlType'] == 'struct' or arg['IdlType'] == 'set' or arg['IdlType'] == 'seq' or arg['IdlType'] == 'dict':
                proxy_file.write('{}')
            elif arg['IdlType'] == 'string':
                proxy_file.write('""')
            else:
                proxy_file.write('0')

            n+=1
        proxy_file.write(');\n\n')

    proxy_file.write("\t\tfinished_ = true;\n\t)\n")
    proxy_file.write('}\n')
    proxy_file.close()

    return True
